<template>
	<div>
		<div style="position: relative; height: 300px" class="my-3">
			<Bar :data="chartData" :options="options" />
		</div>
		<LegendList :legends="legends" />
	</div>
</template>

<script>
import { Bar } from "vue-chartjs";
import { BarController, BarElement, CategoryScale, LinearScale, Legend, Tooltip } from "chart.js";
import { registerChartComponents, commonOptions, tooltipLabelColor } from "./chartConfig";
import LegendList from "./LegendList.vue";
import formatter, { POWER_UNIT } from "../../mixins/formatter";
import colors from "../../colors";
import { GROUPS, PERIODS } from "./types";

registerChartComponents([BarController, BarElement, CategoryScale, LinearScale, Legend, Tooltip]);

export default {
	name: "EnergyHistoryChart",
	components: { Bar, LegendList },
	mixins: [formatter],
	props: {
		sessions: { type: Array, default: () => [] },
		groupBy: { type: String, default: GROUPS.NONE },
		period: { type: String, default: PERIODS.TOTAL },
		colorMappings: { type: Object, default: () => ({ loadpoint: {}, vehicle: {} }) },
	},
	computed: {
		firstDay() {
			if (this.sessions.length === 0) {
				return null;
			}
			return new Date(this.sessions[0].created);
		},
		month() {
			return this.firstDay?.getMonth() + 1;
		},
		year() {
			return this.firstDay?.getFullYear();
		},
		lastDay() {
			if (this.sessions.length === 0) {
				return null;
			}
			return new Date(this.sessions[this.sessions.length - 1].created);
		},
		chartData() {
			console.log("update energy history data");
			const result = {};
			const groups = new Set();

			if (this.sessions.length > 0) {
				//const lastDay = new Date(this.year, this.month, 0);
				//const daysInMonth = this.lastDay.getDate();
				let xFrom, xTo;
				if (this.period === PERIODS.TOTAL) {
					xFrom = this.firstDay.getFullYear();
					xTo = this.lastDay.getFullYear();
				} else if (this.period === PERIODS.YEAR) {
					xFrom = 1;
					xTo = 12;
				} else {
					xFrom = 1;
					xTo = new Date(
						this.lastDay.getFullYear(),
						this.lastDay.getMonth() + 1,
						0
					).getDate();
				}

				// initialize result with empty arrays
				for (let i = xFrom; i <= xTo; i++) {
					result[i] = {};
				}

				// Populate with actual data
				this.sessions.forEach((session) => {
					let index;
					const date = new Date(session.created);
					if (this.period === PERIODS.MONTH) {
						index = date.getDate();
					} else if (this.period === PERIODS.YEAR) {
						index = date.getMonth() + 1;
					} else {
						index = date.getFullYear();
					}

					if (this.groupBy === GROUPS.NONE) {
						groups.add("grid");
						groups.add("self");
						const charged = session.chargedEnergy;
						const self = (charged / 100) * session.solarPercentage;
						const grid = charged - self;
						result[index].self = (result[index].self || 0) + self;
						result[index].grid = (result[index].grid || 0) + grid;
					} else {
						const groupKey = session[this.groupBy];
						groups.add(groupKey);
						result[index][groupKey] =
							(result[index][groupKey] || 0) + session.chargedEnergy;
					}
				});
			}

			const datasets = Array.from(groups).map((group) => {
				const colorGroup = this.groupBy === GROUPS.NONE ? "solar" : this.groupBy;
				const backgroundColor = this.colorMappings[colorGroup][group];
				const label =
					this.groupBy === GROUPS.NONE ? this.$t(`sessions.group.${group}`) : group;

				return {
					backgroundColor,
					label,
					data: Object.values(result).map((day) => day[group] || 0),
					borderRadius: (context) => {
						const threshold = 0.04; // 400 Wh
						const { dataIndex, datasetIndex } = context;
						const currentValue = context.dataset.data[dataIndex];
						const previousValuesExist = context.chart.data.datasets
							.slice(datasetIndex + 1)
							.some((dataset) => (dataset?.data[dataIndex] || 0) > threshold);
						return currentValue > threshold && !previousValuesExist
							? { topLeft: 10, topRight: 10 }
							: { topLeft: 0, topRight: 0 };
					},
				};
			});

			return {
				labels: Object.keys(result),
				datasets: datasets,
			};
		},
		legends() {
			return this.chartData.datasets.map((dataset) => ({
				label: dataset.label,
				color: dataset.backgroundColor,
				value: this.fmtWh(
					dataset.data.reduce((acc, curr) => acc + curr, 0) * 1e3,
					POWER_UNIT.AUTO
				),
			}));
		},
		options() {
			// capture vue component this to be used in chartjs callbacks
			// eslint-disable-next-line @typescript-eslint/no-this-alias
			const vThis = this;
			return {
				...commonOptions,
				locale: this.$i18n?.locale,
				color: colors.text,
				borderSkipped: false,
				maxBarThickness: 40,
				animation: false,
				plugins: {
					...commonOptions.plugins,
					tooltip: {
						...commonOptions.plugins.tooltip,
						axis: "x",
						positioner: (context) => {
							const { chart, tooltipPosition } = context;
							const { tooltip } = chart;
							const { width, height } = tooltip;
							const { x, y } = tooltipPosition();
							const { innerWidth, innerHeight } = window;

							return {
								x: Math.min(x, innerWidth - width),
								y: Math.min(y, innerHeight - height),
							};
						},
						callbacks: {
							title: (tooltipItem) => {
								const { label } = tooltipItem[0];
								if (this.period === PERIODS.TOTAL) {
									return label;
								} else if (this.period === PERIODS.YEAR) {
									const date = new Date(this.year, label - 1, 1);
									return this.fmtMonth(date);
								} else {
									const date = new Date(this.year, this.month - 1, label);
									return this.fmtDayMonth(date);
								}
							},
							label: (tooltipItem) => {
								const datasetLabel = tooltipItem.dataset.label || "";
								const value = tooltipItem.raw || 0;
								return value
									? `${datasetLabel}: ${this.fmtWh(value * 1e3, POWER_UNIT.AUTO)}`
									: null;
							},
							labelColor: tooltipLabelColor(false),
							labelPointStyle: function () {
								return {
									pointStyle: "circle",
								};
							},
						},
						itemSort: function (a, b) {
							return b.datasetIndex - a.datasetIndex;
						},
					},
				},
				scales: {
					x: {
						stacked: true,
						border: { display: false },
						grid: { display: false },
						ticks: {
							color: colors.muted,
							callback: function (value) {
								return vThis.period === PERIODS.YEAR
									? vThis.fmtMonth(new Date(vThis.year, value, 1), true)
									: this.getLabelForValue(value);
							},
						},
					},
					y: {
						stacked: true,
						border: { display: false },
						grid: { color: colors.border },
						title: {
							text: "kWh",
							display: true,
							color: colors.muted,
						},
						ticks: {
							callback: (value) => this.fmtWh(value * 1e3, POWER_UNIT.KW, false, 0),
							color: colors.muted,
							maxTicksLimit: 6,
						},
						position: "right",
						min: 0,
					},
				},
			};
		},
	},
};
</script>
